Skip to content

feat(public): docs content contract (/api/public/docs) + decommission Redoc#3878

Merged
PierreBrisorgueil merged 6 commits into
masterfrom
feat/public-docs-contract-decom-redoc
Jun 15, 2026
Merged

feat(public): docs content contract (/api/public/docs) + decommission Redoc#3878
PierreBrisorgueil merged 6 commits into
masterfrom
feat/public-docs-contract-decom-redoc

Conversation

@PierreBrisorgueil

@PierreBrisorgueil PierreBrisorgueil commented Jun 15, 2026

Copy link
Copy Markdown
Contributor

What

  1. Generic docs content contract — new modules/public:
    • GET /api/public/docs{ categories:[{ id, label, order, guides:[{ slug, title, persona, order, summary }] }] }, grouped via config.docs.guideSections (neutral defaults; persona defaults to ['all']).
    • GET /api/public/docs/:slug.md → raw text/markdown; unknown slug → 404. Unauthenticated, rate-limited (limiters.api), ~5min in-process cache.
    • Parses existing per-module doc/guides/*.md via lib/helpers/guides.js; ships 2 generic sample guides (modules/home/doc/guides/) so it works out of the box.
  2. Decommission Redoc (lib/services/express.js): removed redoc-express + the /api/docs UI route + theme/customCss; initSwaggerinitApiSpec. Kept the merged OpenAPI spec at /api/spec.json + the guides-into-spec merge + the swagger.enable/env gate. Dropped redoc-express from deps.
  3. Dropped the now-dead /api/docs audit skip-prefix.

Why

A downstream consumer can now build its own docs UI from the contract + render /api/spec.json itself (in-theme), instead of the bundled Redoc page. Spec-first.

Tests

New modules/public unit + integration (the integration test asserts the real wire shape against the real sample guides, not a mock). Updated express.docs/env-gate/core tests (/api/docs 404, /api/spec.json preserved). Full unit + integration green locally; coverage on the new module 100%/98.9%.

Closes #3877

Summary by CodeRabbit

  • New Features

    • Added public, unauthenticated documentation endpoints for browsing guides and retrieving raw markdown.
    • Added welcome and quickstart guides for API users.
    • Replaced Redoc UI with OpenAPI JSON spec endpoint.
  • Chores

    • Removed redoc-express dependency.
    • Updated audit logging to exclude spec endpoint from tracking.

Add a public, unauthenticated docs module serving:
  - GET /api/public/docs -> { categories: [{ id, label, order,
    guides: [{ slug, title, persona, order, summary }] }] }
  - GET /api/public/docs/:slug.md -> raw text/markdown (unknown slug -> 404)

Guides are discovered from the existing config.files.guides glob
(modules/*/doc/guides/*.md) and grouped via the config.docs.guideSections
primitive (prefix-range sections, optional per-section persona; persona
defaults to ['all']). Guides outside every range fall back to their
capitalised module name so none is ever dropped. Rate-limited (limiters.api),
~5min in-process TTL cache.

Ship two neutral sample guides (00-welcome, 01-quickstart) under
modules/home/doc/guides and a neutral docs.guideSections default so the
endpoint returns a working tree out of the box.
Remove the redoc-express dependency and the /api/docs Redoc UI mount, its
theme + custom-CSS blocks, and the config.docs.redocTheme merge. Rename
initSwagger -> initApiSpec; the OpenAPI JSON spec endpoint (/api/spec.json)
is now the documentation surface and keeps the YAML merge, config-injected
info/servers/x-logo, and the markdown guides merge into info.description.

The config.swagger.enable + non-dev env gate is unchanged (now gates
spec-serving only). Tests: drop the Redoc-UI assertions, strengthen the
spec assertions, and add coverage asserting /api/docs is no longer mounted.
Copilot AI review requested due to automatic review settings June 15, 2026 13:12
@coderabbitai

coderabbitai Bot commented Jun 15, 2026

Copy link
Copy Markdown

Review Change Stack

Warning

Review limit reached

@PierreBrisorgueil, we couldn't start this review because you've reached your PR review rate limit.

More reviews will be available in 37 minutes. Learn how PR review limits work.

Your organization has used up its prepaid credits, and credit purchases are no longer available. Enable the review add-on in the billing tab to keep reviews running — you're only billed for reviews past your plan's rate limits ($0.25/file).

⌛ How to resolve this issue?

After more reviews become available, a review can be triggered using the @coderabbitai review command as a PR comment. Alternatively, push new commits to this PR.

We recommend that you space out your commits to avoid hitting the rate limit.

🚦 How do rate limits work?

CodeRabbit enforces hourly rate limits for each developer per organization.

Our paid plans include higher PR review limits than trial, open-source, and free plans. In all cases, reviews become available again over time. During sustained high-volume PR review activity, CodeRabbit may temporarily slow when the next review becomes available.

Please see our Fair Usage Limits Policy for further information.

ℹ️ Review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: ASSERTIVE

Plan: Pro

Run ID: e302fad0-35ec-4de4-9654-47fa5e6f1832

📥 Commits

Reviewing files that changed from the base of the PR and between 03a7845 and 4c1636e.

📒 Files selected for processing (11)
  • config/defaults/development.config.js
  • modules/core/tests/core.integration.tests.js
  • modules/public/controllers/public.docs.controller.js
  • modules/public/helpers/public.docs.tree.js
  • modules/public/routes/public.docs.routes.js
  • modules/public/services/public.docs.service.js
  • modules/public/tests/public.docs.controller.unit.tests.js
  • modules/public/tests/public.docs.integration.tests.js
  • modules/public/tests/public.docs.service.fallback.unit.tests.js
  • modules/public/tests/public.docs.service.unit.tests.js
  • modules/public/tests/public.docs.tree.unit.tests.js

Walkthrough

Removes the redoc-express dependency and /api/docs Redoc UI route, replacing initSwagger with initApiSpec that serves the merged OpenAPI spec at GET /api/spec.json. Simultaneously adds unauthenticated GET /api/public/docs and GET /api/public/docs/:slug.md endpoints backed by a TTL-cached service that parses on-disk markdown guides into a structured category tree driven by config.docs.guideSections.

Changes

Redoc Decommission → initApiSpec

Layer / File(s) Summary
initApiSpec implementation and export
lib/services/express.js, lib/helpers/guides.js, package.json
Removes redoc-express import and Redoc UI setup. Introduces initApiSpec that merges YAML files via _.mergeWith, injects spec.info/spec.servers/x-logo, merges markdown guides into spec.info.description, and mounts GET /api/spec.json. Updates the startup call site, default export, and guides.js comments to reference OpenAPI viewer instead of Redoc. Drops redoc-express from package.json.
initApiSpec test suite updates
lib/services/tests/express.docs.unit.tests.js, lib/services/tests/express.docsEnvGate.unit.tests.js, modules/core/tests/core.unit.tests.js, modules/core/tests/core.integration.tests.js
Rewires all Swagger-related test suites from initSwagger to initApiSpec. Asserts /api/spec.json is mounted in dev-grade environments, /api/docs is never registered in any environment, and validates x-logo injection, guide merge, YAML failure, and env-gating behaviors.
Audit middleware SKIP_PREFIXES update
modules/audit/middlewares/audit.middleware.js, modules/audit/tests/audit.middleware.unit.tests.js
Removes /api/docs from SKIP_PREFIXES, leaving only /public, /favicon, and /api/health as skipped audit-capture prefixes. Updates the corresponding unit test.

Public Docs Content Contract

Layer / File(s) Summary
Docs tree helper and sample guides
modules/public/helpers/publicDocs.tree.js, modules/home/doc/guides/00-welcome.md, modules/home/doc/guides/01-quickstart.md
Adds publicDocs.tree.js with path/markdown parsing helpers (slugFromPath, prefixFromPath, titleFromMarkdown, stripLeadingH1, firstParagraph), loadGuideEntries for filesystem reads with error-skipping and numeric-prefix sorting, and buildDocsTree for section-based category grouping with persona and module-name fallbacks. Ships two sample guides that populate the tree out of the box.
PublicDocsService with TTL cache
modules/public/services/publicDocs.service.js, config/defaults/development.config.js
Introduces the service with a single-slot TTL in-process cache, defensive config resolvers for guideFiles/guideSections, synchronous compute() with duplicate-slug logging, load() with cache-stampede prevention, and public methods getTree, getMarkdown, clearCache. Adds docs.guideSections "Get Started" section to the development config.
Controller and route registration
modules/public/controllers/publicDocs.controller.js, modules/public/routes/publicDocs.route.js
Adds tree (200/503) and raw (200/404/503) async handlers setting Cache-Control and Content-Type: text/markdown; charset=utf-8. Wires GET /api/public/docs and GET /api/public/docs/:slug.md with limiters.api rate limiting.
Public docs unit and integration tests
modules/public/tests/publicDocs.tree.unit.tests.js, modules/public/tests/publicDocs.service.unit.tests.js, modules/public/tests/publicDocs.service.fallback.unit.tests.js, modules/public/tests/publicDocs.controller.unit.tests.js, modules/public/tests/publicDocs.integration.tests.js
Adds unit tests for tree helper path parsers, loadGuideEntries, and buildDocsTree; service caching, getMarkdown, duplicate slug handling, and config-absent fallbacks; controller response shaping and error envelopes. Integration tests validate live endpoint wire shape, guide grouping, raw markdown content stripping, 404 behavior, and absence of YAML front-matter leakage.

Sequence Diagram

sequenceDiagram
  participant Client
  participant PublicDocsRoute as GET /api/public/docs
  participant PublicDocsController
  participant PublicDocsService
  participant DocsTreeHelper as publicDocs.tree.js
  participant Filesystem as doc/guides/*.md

  Client->>PublicDocsRoute: GET /api/public/docs (unauthenticated)
  PublicDocsRoute->>PublicDocsController: tree(req, res)
  PublicDocsController->>PublicDocsService: getTree()
  PublicDocsService->>PublicDocsService: check TTL cache
  alt cache miss
    PublicDocsService->>DocsTreeHelper: loadGuideEntries(guideFiles)
    DocsTreeHelper->>Filesystem: readFileSync each .md
    Filesystem-->>DocsTreeHelper: markdown content
    DocsTreeHelper-->>PublicDocsService: sorted guide entries
    PublicDocsService->>DocsTreeHelper: buildDocsTree(entries, guideSections)
    DocsTreeHelper-->>PublicDocsService: { categories }
    PublicDocsService->>PublicDocsService: stamp expiresAt, build slug index
  end
  PublicDocsService-->>PublicDocsController: { categories }
  PublicDocsController->>Client: 200 + Cache-Control + success envelope
Loading

Estimated code review effort

🎯 4 (Complex) | ⏱️ ~60 minutes

Possibly related PRs

  • pierreb-devkit/Node#3453: Both PRs modify lib/services/express.js and lib/helpers/guides.js to merge markdown guides into spec.info.description; this PR renames the entrypoint to initApiSpec while PR #3453 introduced the guide-loading/merging logic.
  • pierreb-devkit/Node#3460: Both PRs affect how markdown guides are loaded and merged into /api/spec.json's info.description; PR #3460 altered the guide-loader tests while this PR changes the wiring entry point to initApiSpec.
  • pierreb-devkit/Node#3318: This PR's removal of /api/docs from modules/audit/middlewares/audit.middleware.js SKIP_PREFIXES directly builds on the audit middleware introduced in PR #3318.

Suggested labels

Feat

🚥 Pre-merge checks | ✅ 5
✅ Passed checks (5 passed)
Check name Status Explanation
Title check ✅ Passed The PR title clearly and concisely describes the two main changes: introducing a public docs content contract and decommissioning Redoc. It is specific, relevant, and directly reflects the primary objectives.
Description check ✅ Passed The PR description covers all required template sections (summary of what/why, scope, validation, guardrails, and notes for reviewers). It clearly explains the new contract, Redoc removal, and testing coverage.
Linked Issues check ✅ Passed All objectives from issue #3877 are met: new public docs endpoints with structured guide tree and raw markdown serving are implemented, Redoc is decommissioned while preserving OpenAPI spec, config-driven grouping works, and sample guides enable out-of-the-box functionality.
Out of Scope Changes check ✅ Passed All changes are directly aligned with issue #3877 objectives. Modifications to express.js, configuration, tests, and audit middleware all serve the goal of decommissioning Redoc and implementing the new docs contract.
Docstring Coverage ✅ Passed No functions found in the changed files to evaluate docstring coverage. Skipping docstring coverage check.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch feat/public-docs-contract-decom-redoc

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands and usage tips.

Copilot AI left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

Introduces a new public module that exposes a generic, config-driven docs content contract (tree + raw markdown) for downstream docs UIs, and removes the bundled Redoc UI while preserving the merged OpenAPI spec endpoint.

Changes:

  • Added GET /api/public/docs (structured categories/guides contract) and GET /api/public/docs/:slug.md (raw markdown) with rate limiting + short in-process caching.
  • Decommissioned Redoc (/api/docs) and renamed docs init from initSwaggerinitApiSpec, keeping /api/spec.json (still env-gated).
  • Added 2 sample markdown guides under modules/home/doc/guides/ and updated unit/integration tests accordingly.

Reviewed changes

Copilot reviewed 21 out of 22 changed files in this pull request and generated 10 comments.

Show a summary per file
File Description
package.json Removes redoc-express dependency.
package-lock.json Lockfile updates reflecting redoc-express removal.
lib/services/express.js Removes /api/docs Redoc wiring; keeps /api/spec.json and renames initializer to initApiSpec.
lib/helpers/guides.js Updates wording from Redoc-specific to generic OpenAPI viewer language.
lib/services/tests/express.docs.unit.tests.js Updates docs tests to validate /api/spec.json behavior and absence of /api/docs.
lib/services/tests/express.docsEnvGate.unit.tests.js Updates env-gate tests for initApiSpec and ensures /api/docs is never mounted.
modules/public/helpers/publicDocs.tree.js Adds markdown parsing + tree builder for the public docs contract.
modules/public/services/publicDocs.service.js Adds TTL cache + guide tree/slug lookup service reading from config.files.guides.
modules/public/controllers/publicDocs.controller.js Adds controllers that wrap the service into the standard response envelope and serve raw markdown.
modules/public/routes/publicDocs.route.js Registers unauthenticated, rate-limited public docs routes.
modules/public/tests/publicDocs.tree.unit.tests.js Unit tests for slug/prefix/module/title/summary parsing and tree assembly.
modules/public/tests/publicDocs.service.unit.tests.js Unit tests for caching + slug lookup with mocked helpers/config.
modules/public/tests/publicDocs.service.fallback.unit.tests.js Unit tests for defensive behavior when config keys are absent.
modules/public/tests/publicDocs.controller.unit.tests.js Unit tests for controller response shaping and error paths.
modules/public/tests/publicDocs.integration.tests.js End-to-end tests verifying the real wire contract and raw markdown behavior.
modules/home/doc/guides/00-welcome.md Adds sample “Welcome” guide.
modules/home/doc/guides/01-quickstart.md Adds sample “Quickstart” guide.
modules/core/tests/core.unit.tests.js Updates core express-service tests for initApiSpec and /api/docs removal.
modules/core/tests/core.integration.tests.js Updates integration coverage to assert /api/spec.json exists and /api/docs is not a Redoc UI.
modules/audit/middlewares/audit.middleware.js Removes /api/docs from audit skip prefixes.
modules/audit/tests/audit.middleware.unit.tests.js Updates audit middleware test for updated skip prefix list.
config/defaults/development.config.js Adds docs.guideSections config used for guide grouping.

Comment thread lib/services/express.js
Comment thread modules/public/services/public.docs.service.js
Comment thread modules/public/services/public.docs.service.js
Comment thread modules/public/helpers/public.docs.tree.js
Comment thread modules/public/helpers/publicDocs.tree.js Outdated
Comment thread modules/public/tests/public.docs.integration.tests.js
Comment thread modules/public/routes/public.docs.routes.js
Comment thread modules/public/controllers/public.docs.controller.js
Comment thread modules/public/services/public.docs.service.js
Comment thread modules/public/helpers/public.docs.tree.js
…dpoints

M1: replace silent last-win Map construction in publicDocs.service compute()
with an explicit loop that logger.warn()s on duplicate slugs.
M2: add comment in load() explaining why no inflight guard is needed
(compute() is fully synchronous — no stampede risk on the event loop).
M3: set Cache-Control: public, max-age=300 on both the tree and raw-markdown
200 responses in publicDocs.controller (matches the 5-min in-process TTL);
add controller unit tests asserting the header is present on 200 and absent
on 404.
L4: document NN-kebab-name.md filename convention in slugFromPath.
L5: add comment in express.docs.unit.tests.js second describe block noting
that every new test must stub loadGuides inline (no module-level mock).
nit: trim docs.guideSections block comment in development.config.js to
one line pointing at the helper JSDoc.
@codecov

codecov Bot commented Jun 15, 2026

Copy link
Copy Markdown

Codecov Report

✅ All modified and coverable lines are covered by tests.
✅ Project coverage is 92.67%. Comparing base (3937953) to head (4c1636e).

Additional details and impacted files
@@            Coverage Diff             @@
##           master    #3878      +/-   ##
==========================================
+ Coverage   92.50%   92.67%   +0.17%     
==========================================
  Files         165      169       +4     
  Lines        5403     5529     +126     
  Branches     1740     1775      +35     
==========================================
+ Hits         4998     5124     +126     
  Misses        325      325              
  Partials       80       80              
Flag Coverage Δ
integration 61.13% <85.71%> (+1.01%) ⬆️
unit 74.22% <96.99%> (+0.54%) ⬆️

Flags with carried forward coverage won't be shown. Click here to find out more.


Continue to review full report in Codecov by Harness.

Legend - Click here to learn more
Δ = absolute <relative> (impact), ø = not affected, ? = missing data
Powered by Codecov. Last update 3937953...4c1636e. Read the comment docs.

🚀 New features to boost your workflow:
  • ❄️ Test Analytics: Detect flaky tests, report on failures, and find test suite problems.
  • 📦 JS Bundle Analysis: Save yourself from yourself by tracking and limiting bundle sizes in JS merges.

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 5

Caution

Some comments are outside the diff and can’t be posted inline due to platform limitations.

⚠️ Outside diff range comments (1)
lib/services/tests/express.docs.unit.tests.js (1)

191-215: ⚠️ Potential issue | 🟡 Minor | ⚡ Quick win

Add complete JSDoc tags to the modified helper functions.

buildMockApp and callSpecRoute are modified helper functions, but their JSDoc blocks don’t include full @param/@returns tags required by the repository rule.

As per coding guidelines: **/*.js: Every new or modified function must have a JSDoc header with @param for each argument and @returns for non-void returns.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@lib/services/tests/express.docs.unit.tests.js` around lines 191 - 215, Add
complete JSDoc tags to both modified helper functions. For buildMockApp, add a
`@returns` tag documenting the returned mock app object with the routes property.
For callSpecRoute, add a `@param` tag documenting the app parameter and a `@returns`
tag documenting the returned spec value. Ensure both functions have their JSDoc
blocks updated to include these required tags per the repository coding
guidelines for modified functions.

Source: Coding guidelines

🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@modules/core/tests/core.integration.tests.js`:
- Around line 316-323: The test for the decommissioned Redoc endpoint is
incorrectly asserting a 200 status code when the intended contract for this PR
is that /api/docs should return 404 (Not Found). Change the expect(200)
assertion to expect(404) in the "should NOT serve a dedicated Redoc UI on
/api/docs (decommissioned)" test to properly validate the decommission contract.
Additionally, ensure the application routing is configured to explicitly return
a 404 response for the /api/docs path rather than falling through to a catch-all
handler, so the test assertion aligns with the actual expected behavior.

In `@modules/public/controllers/publicDocs.controller.js`:
- Around line 8-16: The JSDoc comments for async controller handlers incorrectly
document `@returns` as {void} instead of {Promise<void>}. Update the `@returns` type
annotation in the JSDoc comment blocks to accurately reflect that these are
async functions returning a Promise. Specifically, at lines 8-16 in the
publicDocs.controller.js file, change `@returns` {void} to `@returns`
{Promise<void>} for the GET /api/public/docs handler. Apply the same fix at
lines 27-34 for any other async handler in the same file that has the same
incorrect `@returns` documentation.

In `@modules/public/services/publicDocs.service.js`:
- Around line 30-38: The functions guideFiles() and guideSections() currently
return the live config arrays directly, which violates their JSDoc contract and
risks mutations leaking into global config state. Clone both arrays before
returning them to ensure defensive copies are provided to downstream consumers.
Use a shallow clone approach (such as spreading with the spread operator or
calling .slice()) for the returned arrays in both functions.

In `@modules/public/tests/publicDocs.controller.unit.tests.js`:
- Around line 20-27: The named function `mockResponse` is missing a JSDoc
header, which is required by repository guidelines. Add a JSDoc block above the
`mockResponse` function that includes a description of its purpose and a
`@returns` tag documenting that it returns a mock response object with chained
methods (status, json, send, set).

In `@modules/public/tests/publicDocs.tree.unit.tests.js`:
- Line 85: The named standalone helper function `write` is missing JSDoc
documentation. Add JSDoc annotations above the `write` function declaration that
includes `@param` tags for both the `name` and `content` parameters (describing
what each parameter represents and its expected type) and a `@returns` tag
describing what the function returns. This ensures compliance with the
repository JSDoc rule for named helper functions in test files.

---

Outside diff comments:
In `@lib/services/tests/express.docs.unit.tests.js`:
- Around line 191-215: Add complete JSDoc tags to both modified helper
functions. For buildMockApp, add a `@returns` tag documenting the returned mock
app object with the routes property. For callSpecRoute, add a `@param` tag
documenting the app parameter and a `@returns` tag documenting the returned spec
value. Ensure both functions have their JSDoc blocks updated to include these
required tags per the repository coding guidelines for modified functions.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: ASSERTIVE

Plan: Pro

Run ID: 8bd1c260-2f8a-425c-b106-c4a67f3c39c2

📥 Commits

Reviewing files that changed from the base of the PR and between 3937953 and 03a7845.

⛔ Files ignored due to path filters (1)
  • package-lock.json is excluded by !**/package-lock.json
📒 Files selected for processing (21)
  • config/defaults/development.config.js
  • lib/helpers/guides.js
  • lib/services/express.js
  • lib/services/tests/express.docs.unit.tests.js
  • lib/services/tests/express.docsEnvGate.unit.tests.js
  • modules/audit/middlewares/audit.middleware.js
  • modules/audit/tests/audit.middleware.unit.tests.js
  • modules/core/tests/core.integration.tests.js
  • modules/core/tests/core.unit.tests.js
  • modules/home/doc/guides/00-welcome.md
  • modules/home/doc/guides/01-quickstart.md
  • modules/public/controllers/publicDocs.controller.js
  • modules/public/helpers/publicDocs.tree.js
  • modules/public/routes/publicDocs.route.js
  • modules/public/services/publicDocs.service.js
  • modules/public/tests/publicDocs.controller.unit.tests.js
  • modules/public/tests/publicDocs.integration.tests.js
  • modules/public/tests/publicDocs.service.fallback.unit.tests.js
  • modules/public/tests/publicDocs.service.unit.tests.js
  • modules/public/tests/publicDocs.tree.unit.tests.js
  • package.json
💤 Files with no reviewable changes (1)
  • package.json

Comment thread modules/core/tests/core.integration.tests.js
Comment thread modules/public/controllers/public.docs.controller.js
Comment thread modules/public/services/publicDocs.service.js Outdated
Comment thread modules/public/tests/public.docs.controller.unit.tests.js
Comment thread modules/public/tests/public.docs.tree.unit.tests.js
…copies + deterministic ordering

F1: git mv all publicDocs.* files to public.docs.{type}.js (dot-separated, module-prefixed,
    routes plural); update every import and reference including test mocks and config comment.
F2/F3: guideFiles() returns [...config.files.guides], guideSections() returns .map(s=>({...s}))
    so callers cannot mutate config state.
F4: [...filePaths].sort() before .map() so fallback index for unprefixed guides is
    platform-stable (glob order is not guaranteed).
F5: sort tiebreaker a.slug.localeCompare(b.slug) for equal numeric prefixes.
F6: remove try/catch in beforeAll — let bootstrap() throw with its real stack trace.
F7: update stale Redoc/reference-sidebar comments in public.docs.tree.js and
    development.config.js (guideSections groups public docs contract only).
@PierreBrisorgueil

Copy link
Copy Markdown
Contributor Author

Addressed review findings (Copilot + CodeRabbit):

  • Naming: renamed publicDocs.*public.docs.* (devkit {module}.{entity}.{type} convention, plural *.routes.js), via git mv (history preserved).
  • Defensive copies: guideFiles()/guideSections() now return copies ([...] / .map(s=>({...s}))) — no shared mutable config state. (Was already fixed in c64335e; the earlier CodeRabbit note referenced a pre-rename revision.)
  • Deterministic ordering: glob list sorted before fallback indexing + sort tiebreaker on equal prefixes.
  • /api/docs decom test: added a positive assertion that it falls through to the generic catch-all landing (200), so the decommission contract is unambiguous.
  • Cache-Control: public, max-age=300 on the docs responses (not on 404).
  • JSDoc on async controller handlers (@returns {Promise<void>}) + test helpers; integration beforeAll no longer masks bootstrap errors.
    CI green; coverage held (new module 100%).

@PierreBrisorgueil

Copy link
Copy Markdown
Contributor Author

@coderabbitai review — all prior findings addressed (naming convention, defensive copies, deterministic ordering, Cache-Control, the /api/docs decom assertion, JSDoc). Please re-review.

@coderabbitai

coderabbitai Bot commented Jun 15, 2026

Copy link
Copy Markdown

Tip

For best results, initiate chat on the files or code changes.

🧩 Analysis chain

🏁 Script executed:

#!/bin/bash
set -euo pipefail

echo "=== Listing modules/public structure ==="
find modules/public -type f | sort

echo ""
echo "=== modules/public/controllers ==="
find modules/public/controllers -type f | sort

echo ""
echo "=== modules/public/services ==="
find modules/public/services -type f | sort

echo ""
echo "=== modules/public/helpers ==="
find modules/public/helpers -type f | sort

echo ""
echo "=== modules/public/routes ==="
find modules/public/routes -type f | sort

echo ""
echo "=== modules/public/tests ==="
find modules/public/tests -type f | sort

Length of output: 1755


🏁 Script executed:

#!/bin/bash
set -euo pipefail

echo "=== Controller file ==="
cat modules/public/controllers/public.docs.controller.js 2>/dev/null || find modules/public/controllers -name "*.js" -exec echo "File: {}" \; -exec cat {} \;

echo ""
echo "=== Service file ==="
find modules/public/services -name "*.js" -exec echo "File: {}" \; -exec cat {} \;

echo ""
echo "=== Helper/tree file ==="
find modules/public/helpers -name "*.js" -exec echo "File: {}" \; -exec cat {} \;

echo ""
echo "=== Routes file ==="
find modules/public/routes -name "*.js" -exec echo "File: {}" \; -exec cat {} \;

Length of output: 19459


🏁 Script executed:

#!/bin/bash
set -euo pipefail

echo "=== Test files ==="
find modules/public/tests -name "*.js" -exec echo "File: {}" \; -exec cat {} \;

Length of output: 23369


🏁 Script executed:

#!/bin/bash
set -euo pipefail

echo "=== lib/services/express.js ==="
cat lib/services/express.js

echo ""
echo "=== lib/helpers/guides.js ==="
cat lib/helpers/guides.js

echo ""
echo "=== config/defaults/development.config.js ==="
cat config/defaults/development.config.js

Length of output: 25683


🏁 Script executed:

#!/bin/bash
set -euo pipefail

echo "=== modules/core/tests/core.integration.tests.js (relevant section) ==="
grep -n "api/docs\|spec.json\|Redoc\|OpenAPI\|decommission\|catch-all\|landing" modules/core/tests/core.integration.tests.js -A 5 -B 2 || true

echo ""
echo "=== modules/audit/middlewares/audit.middleware.js ==="
cat modules/audit/middlewares/audit.middleware.js

echo ""
echo "=== modules/home/doc/guides/ ==="
ls modules/home/doc/guides/ 2>/dev/null || echo "not found"
find . -path "*/home/doc/guides/*" -type f 2>/dev/null | sort

echo ""
echo "=== package.json (dependencies section) ==="
python3 -c "
import json
with open('package.json') as f:
    pkg = json.load(f)
deps = pkg.get('dependencies', {})
for k in sorted(deps.keys()):
    print(f'{k}: {deps[k]}')
" 2>/dev/null || grep -A 3 '"redoc\|posthog\|resend' package.json || true

Length of output: 8716



@PierreBrisorgueil: Thorough re-review completed against the current branch. All previously flagged findings are confirmed addressed.


Review Summary

Naming convention
All files follow {module}.{entity}.{type}public.docs.controller.js, public.docs.service.js, public.docs.tree.js, public.docs.routes.js (plural) — and tests mirror that pattern.

Defensive copies
guideFiles() returns [...config.files.guides] and guideSections() returns .map((s) => ({ ...s })). Neither leaks mutable config state.

Deterministic ordering
loadGuideEntries pre-sorts the raw file paths with [...filePaths].sort() before assigning fallback indices, then applies a stable final sort with a.order - b.order || a.slug.localeCompare(b.slug) as the tiebreaker. Glob non-determinism is fully neutralised.

/api/docs decom test
The integration test makes a positive assertion — expect(res.text).toContain('Devkit Node Api') — confirming the 200 is the generic catch-all, not a surviving Redoc surface.

Cache-Control
public, max-age=300 is set before the 200 on both tree and raw; the 404 path returns without touching that header.

JSDoc
Both tree and raw carry @returns {Promise<void>}. mockResponse in the controller unit test has a typed @returns as well.

Additional checks that passed:

  • DEFAULT_PERSONA is Object.freeze(['all']); sectionForEntry always returns a copy ([...match.persona] / [...DEFAULT_PERSONA]).
  • compute() is fully synchronous (fs.readFileSync); the comment explaining why no inflight guard is needed is present and correct.
  • redoc-express is absent from package.json; /api/docs removed from SKIP_PREFIXES.
  • Fallback integration: config.docs.guideSections prefixMin: 0, prefixMax: 9 covers both shipped guides (00-welcome, 01-quickstart), and the integration test asserts the real get-started grouping and ['all'] persona.
  • Every test file stubs loadGuides inline (no module-level mocks).

One minor note (no action required): buildDocsTree assigns categories in first-guide discovery order rather than sections array order. With the current design — entries sorted by numeric prefix, sections using non-overlapping prefix ranges — output category order naturally tracks config section order. Just worth documenting if the schema ever allows overlapping ranges.

The implementation is clean, well-structured, and the contract is unambiguous. Happy to approve.

[approve]

@PierreBrisorgueil PierreBrisorgueil merged commit 580f6a3 into master Jun 15, 2026
8 checks passed
@PierreBrisorgueil PierreBrisorgueil deleted the feat/public-docs-contract-decom-redoc branch June 15, 2026 17:55
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

✨ Generic docs content contract (/api/public/docs) + decommission Redoc

2 participants